查看原文
其他

如何设计打造金融场景下的业务平台

王佳源 58技术 2022-03-15


导语

本文讲述了智能借贷API平台从无到有的设计思路,以及如何在不同机构间流程、节点不同,接口质量、可用性不同的情况下,抽象共性、提升效率,屏蔽差异、聚焦业务,资源隔离、可靠稳定,分治而为、多点开花,一步步演化出最终的架构内容。


背景





在我们之前的金融业务体系下,面向C端用户并且用户群体最大的产品是贷款超市,贷款超市聚合了众多H5贷款产品,定位是向外部现金贷平台提供流量、用户的平台。
为了更好的承接集团流量,服务不同客群,把流量尽快的变现,同时丰富、训练我们自身的风控数据、能力,我们开展启动了API机构合作业务模式。
API 机构合做具体是指,展示机构的品牌、金融产品,机构独立风控决策、完成放款;我们金融平台连接机构和用户,为机构提供匹配的用户,同时为用户提供从授信到还款所有环节的服务,形成流程闭环。





H5纯导流模式


设计思路

1、What? How? Now!
API机构合作平台,在互联网金融领域,竞品公司早已经有此类业务,但对我们来说还是个新业务形态,在产品演进和技术实现上都是从0到1的开始。
1)什么是机构合作平台,什么是API模式,它和通常的业务系统有什么区别?很显然,之前的H5导流贷超系统肯定不是我们想要的,完全闭环的自营贷款业务也不适合API模式。因此在做技术架构设计的时候,我们不断的问自己,API平台模式都需要有什么样的功能,它核心最重要的能力是什么?从业务场景特性上,它需要具备什么;从产品定位上,它应该有什么;从技术架构上,我们来怎么设计、划分?
2)在头脑中大致对API模式有了概念和思考之后,我们想到我们首先需要定义一套合理的流程、接口与机构去对接;其次我们在接口规范化之后还要保证交互数据的安全性;不同机构在流程上可能会有一定的差异性,我们需要具备流程编排的能力;同一接口调用不同机构时在参数上会有不同,我们也需要对上游屏蔽参数差异化;对于一些通用共性的功能,是否可以抽象出来并且灵活可配置化;在系统可用性上,我们不仅要保证自己系统的高可用,还要对依赖的机构第三方接口建立完善的监控、隔离甚至是熔断机制,等等。
3)有了功能模块的规划,接下来要进行技术选型。例如加密验签方式的选择、数据存储介质的选择(表单特性)、流程编排能力的选择、接口或线程隔离的技术实现等等。
4)在实际开发中,因为时间、资源等因素,我们不可能一开始就做一个大而全的系统,我们的产品业务肯定需要逐步完善和优化,我们的支撑系统也会随之不断迭代升级。所以在产品上线初期,我们要在系统架构横向及纵向上考虑折中。
 
2、API机构合作平台—业务流程





业务流程长,节点多,不同机构差异化较多

3、API机构合作平台—时序图




简化版时序图,实际逻辑更加复杂,与内部、外部交互多


演绎升级后的API体系

1、技术架构图
这个是整体系统的架构规划图,在第一期上线的时候,因为时间、资源等因素,部分系统我们只是简单实现,或者是在系统层面未完全拆分,考虑到了让产品形态快速部署、快速上线。在之后的几个月,我们清晰划分了各领域边界,提炼抽象并引入了较多的通用能力,如流程服务、订单服务、统一配置服务、密钥服务等,接下来也会重点介绍。(篇幅原因,细节代码就不展示了)




以中台能力为基础的API机构合作平台

1)流程服务:在文章开始时曾描述过API业务的流程节点有10个以上,且每个机构的流程分支又有不同,例如绑卡节点可能在授信审核前也有可能在审核后,有的机构只有一推没有二推,还有的机构放款后需要二次提现,等等。在接入流程服务之前,进件流程里对业务节点的驱动是写在代码中,代码臃肿且对不同机构复用性差,现在对节点的驱动由流程服务进行驱动,并做到了不同机构节点可配置化。





比如百度与360机构对业务流程要求就不同

流程服务是由状态服务+流程引擎组成。 
状态服务是抽象简化版的状态机,负责订单状态的管理、状态轨迹记录,给业务方提供事件码和页面码(pageCode),功能包含调用流程引擎进行流程实例初始化、状态的记录、根据业务传入事件码相应的调用流程引擎进行节点的驱动、节点内部小状态的管理。节点内小状态的流转不依赖流程引擎,比如借款节点,提交借款后的结果可能是增验、短验、审核、成功、失败,这些对于一个大节点来说都属于内部的小状态,由机构回调接口触发,在节点内部完成流转即可,当订单状态抵达当前大节点的终态,此时再调用流程引擎进行节点的驱动。如果把节点理解成一个“点”,点与点之间的触发条件理解成“边”,上述这样设计的目的是为了减少不同机构因为“边”条件的不同而使得流程配置复杂化。
流程引擎核心是解决不同机构间进件流程不同的情况,第一版是基于Activiti5实现,后来想简化对Activiti5较重存储结构的依赖,以及把状态与流程节点解耦,相关同事自研重构了流程引擎服务,在API业务中只承载每家机构节点的配置及节点的流转。
我们引入流程引擎,就是解决动态流程的问题。流程的每个节点,都代表一个完整的事件,相互独立,节点对每一个机构来说又是可复用的,功能内聚的。




API业务与流程服务的调用,订单状态也是由状态服务管理
2)代理服务:机构代理的主要目的是为了屏蔽不同机构对接口定义、调用配置不同带来的差异化,以及可监控。随着机构接入的增多,实际中我们也遇到部分机构因为上线或自身内部原因导致部分接口超时、不可用等,所以为了保证个别机构的问题不会影响整体业务的可用性,我们之后在对机构的底层调用做了异步化和资源隔离的优化改造。
通常我们说的RPC异步调用,是为了解放Client,让Client可以去做其他的事情,但 Server端当前线程还是阻塞、占用状态,等待逻辑处理完才Callback到Client。在这里,我们进一步优化异步方式,当调用请求到达Server时,Server线程会把任务丢给自定义线程池,当前线程可以释放去接收下一个任务,自定义线程池里的线程处理完底层逻辑后,带上客户端SessionID,通过Channel写回Client端。
在资源隔离上,每个机构分配一个自定义线程池,只处理该机构的调用请求,互不干扰,对于某些在队列中停留过长的任务,线程获取到任务后也会被丢弃,减少资源浪费,我们对“队列数量”也做了监控预警,实时观测。另外,每个机构的http连接池也是独立的,及时某些机构的接口异常或超时,对其余机构的连接也不会有影响。
上线前我们在准线上环境进行压测,模拟10家机构,9家按照目前线上机构的http接口耗时平均值200ms,1家在【200ms,5s】之间取随机,压测结果是单台机器QPS2700+,且异常的那家机构调用,没有丝毫影响到其余9家的接口调用。








3)密钥服务:该服务设计的目的是改变了之前分散的、配置式的密钥使用方式集中由该服务分配、管理机构密钥,提供基础加解密算法包,提供通用Http通信加解密、签名和验签能力。
具体的加解密算法,我们是采用的AES+RSA结合的方式。RSA算法密钥管理要明显优于AES算法,但是运行速度慢,可以用于签名操作;AES加密速度快,但是在网络传输过程中如何安全管理AES密钥是保证AES加密安全的重要环节。所以我们用这两种算法互补,来保证安全性,用RSA来加密传输AES的秘钥,用AES来加密数据,两者相互结合,优势互补。




4)订单服务:原先对订单数据CRUD,都是在业务逻辑层,随着上层服务根据业务属性进一步纵向拆分,如果多个服务都对订单DB实例进行操作会有复杂性扩散的问题。所以我们把对订单数据的CRUD下沉成数据服务,收敛读写能力,统一读写、缓存、分库分表控制,屏蔽存储细节,无感扩容及容灾。我们这个订单微服务没有太多业务逻辑,在设计初期不是最重点方向,实现上相比整体架构中的其他系统要简单一些。这里想延伸交流一下在设计数据应用层时,有几个点可能需要考虑的地方:a)订单id一定要是全局唯一id,实现方法有很多如雪花、ZK、Redis,目的是分库分表后依然可以唯一标识某条数据,同时有可能需要在上下游服务场景中也保证id局部唯一、接口幂等;b)订单服务承载的业务是差异化的还是相对固定的,这决定我们对DB的选型是noSchema还是schema,通常来说我们是尽可能抽取共性,差异化的内容异构存储;c)短期内是否分库分表,这需要我们对业务1~2年的数据量支撑做计算和预估,同时考虑数据是否需要永久保存,分表的维度需要考虑查询维度、冷热数据等。分表的方式有很多,重点在于后续表的灵活扩容及数据均匀;d)在存储选型上,我们还要考虑读写比例、吞吐量、扩容机制、业务场景对一致性和事务的要求,是否还需要结合索引引擎一起使用等等才能决定是选择关系型还是非关系型甚至是KV类型;
在升级后的订单系统上线时,我们还对一期的100多万订单数据做了线上迁移,迁移的核心方案是首先通过Canal异构增量数据,数据校验无问题后,通过开发的数据迁移工具对历史数据进行批量迁移,迁移完再用对比工具进行数据对比。这样的方案好处是批量迁移只需一次,线上数据平滑迁移,保证系统持续服务。
5)组件系统:组件系统其实是由组件模块、机构配置(在配置服务中)、表单服务、码值服务一起关联使用的,每个机构对于授信进件所需要填写的表单数据项都不同,甚至在补充信息数据项中里的字段列表、码值选项也都不同,所以这里引入了支持noSchema的表单服务、可自定义配置的码值服务。对于不同机构存储字段差异化、字段选项自定义化,都可以通过配置的方式使用这些通用能力快速接入不同机构。
6)监控系统:针对第三方机构接口质量的不确定性,我们建立了较完善的监控体系。在性能方面,对机构接口耗时、服务间调用耗时建立监控;在异常方面,对机构接口可用性、机构返回code码、接口参数体格式、字段异常建立监控;在业务方面,对于订单在中间态停留的时间、终态的比例,进件量、组件填写量的同比波动,授信、用信转化同比转化率等等,也建立了多维度监控。
 
2、Open API文档




我们对外接口定义标准是:
1)单一职责,原子化,避免大而全接口,例如从截图能看到,对借款订单的状态通知(审核结果通知、放款通知等),虽然都是更新订单状态字段,但因为它们属于不同阶段节点的结果通知,所以我们进行分开定义,清晰职责,避免机构错调、漏调;
2)接口意义明确,无歧义,相对机构来说我们是平台,所以我们的文档一定要语义明确严谨,规范标准;
3)接口兼容原则及版本管理,比如内部系统改造升级但不影响机构对我们OpenAPi接口的调用,比如/v1/bandCard,/v2/bandCard;
4)接口安全性考虑,AES、RSA加密传输,敏感信息脱敏;
5)接口风格保持一致,最外层为系统及code码,内层为业务code码;
6)接口幂等性设计,读接口天然具有幂等性,写接口需要调用方提供唯一标识,如同一笔授信推送,无论推送多少次,返回结果都应该是一样的;
7)接口参数简单够用即可,不要重复传递,高内聚;
8)接口适当留有一定扩展性,权衡折中。

3、效果总结
项目一期上线后,随着对接机构越来越多、业务发展越来越成熟,我们也在不断反思现有架构体系、业务流程,之后经过二个月的重构升级,二期上线后:
1)机构对接周期由1个月缩短到8工作日,效率提升150%,人力资源占用减少70%;
2)对接机构的流程、组件、属性、排序展示、限量、监控等,均可在配置后台灵活配置;
3)截止到目前共接入外部机构25+家,日进件单量从最初x百单到x万单,每日放款金额过千万,进件量、各环节转化率都在稳步持续提升中。


总结

API机构合作平台,从项目开始到现在也有一年时间。对这样一个架构体系从0到1,又演化升级为目前的最终架构,过程中思考、总结了很多。 
1)一个好的业务架构不是一开始就能搭建出来的,尤其是从0到1,在基础设施不足、项目时间紧的情况下,可能要采取一定的折中原则,先推进上线,快速试错,只有活下来才是有意义的,我们可以随着业务的发展,不断的迭代优化、演绎升级架构。
2)领域的划分、服务的拆分粒度、通用能力的抽取,整体架构也是需要伴随业务的发展,技术视角才逐步清晰,对业务有了更深的理解,才能纵观全局来规划建设我们的平台能力。
3)架构在做系统拆分的时候,既要从横向维度进行拆分,也要从纵向维度拆分。横向拆分,在服务层面,把展现层、业务逻辑层、数据应用层清晰的划分开,业务逻辑向上抽取,基础服务、数据服务下沉,其次是前后端分离;纵向拆分,比如按业务领域拆分可以分为用户进件、运营后台系统,按基础能力拆分可以分为订单服务、组件服务。
4)规划中台,把业务中共性的部分抽象出来,避免重复造轮子,比如组件服务、流程服务、表单服务等,都是金融类业务可以复用的,可以快速推进业务的发展。
5)业务架构并非没有技术含量,因为我们不仅要懂业务,还要能够理解所涉及技术的原理和适用场景,这才能更好的做技术选型。把复杂的业务拆分、抽象、分层,避免过早陷入细节,要对业务的发展有预判能力,对很多不确定的事情做出判断和选择。
金融业务技术的根本核心要素是安全和稳定,所以在后续的优化迭代里,我们在安全、系统稳定性方面做了很多事情,例如RSA+AES加密、RPC服务底层异步化+隔离、多维度立体化的监控等等。


作者简介
王佳源, 58同城技术经理,消费金融平台业务团队技术组长。




END

阅读推荐

前端埋点统一接入方案实践
招才猫直聘安卓DU动态框架实现
Hermes推送系统架构及实施(iOS)
租房业务APP后端服务重构之路
58同城宝实时数仓建设实践
Android字节码优化工具redex初探



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存